////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    EnchantedGardenGuts.cpp	     							  //
//  Developer:   Ryan Sauls												      //
//  Create date: 9/7/2006                                                     //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Enchanted Garden.            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2006 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "EnchantedGardenGuts.h"

#pragma region Static Array Initializations

////////////////////////////////////////////////////////////////////////////
/// EnchantedGardenGuts::::g_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Nine, use the following code snippet:
///
///		SpinResult result = g_aLineWinSpinResults[ ICON_NINE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_NINE.
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::SpinResult EnchantedGardenGuts::g_aLineWinSpinResults[NUM_VALID_ICONS][6] = 
{
// Number of icons:		0				1					2						3							4						5
/* ICON_NINE			*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_NINE,				EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_TEN				*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_TEN,				EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_JACK			*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_JACK,				EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_QUEEN			*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_QUEEN,			EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_KING			*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_KING,				EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_ACE				*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_THREE_ACE,				EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_RING			*/{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_RING,			EVT_THREE_RING,				EVT_FOUR_RING,			EVT_FIVE_RING		},
/* ICON_JEWEL			*/{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_JEWEL,			EVT_THREE_JEWEL,			EVT_FOUR_JEWEL,			EVT_FIVE_JEWEL		},
/* ICON_GARDEN			*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,					EVT_LOSER,				EVT_LOSER			},
/* ICON_FAIRY_PRINCESS	*/{ EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,					EVT_LOSER,				EVT_LOSER			},
/* ICON_BUTTERFLY		*/{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_BUTTERFLY,		EVT_THREE_BUTTERFLY,		EVT_FOUR_BUTTERFLY,		EVT_FIVE_BUTTERFLY	},
/* ICON_UNICORN			*/{ EVT_LOSER,	EVT_LOSER,			EVT_TWO_UNICORN,		EVT_THREE_UNICORN,			EVT_FOUR_UNICORN,		EVT_FIVE_UNICORN	}
};

////////////////////////////////////////////////////////////////////////////
/// EnchantedGardenGuts::g_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Garden is the only
/// scatter win.  So to get the SpinResult for 5 Garden, use the
/// following code snippet:
///
///		SpinResult result = g_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_GARDEN.
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::SpinResult EnchantedGardenGuts::g_aScatterWinSpinResults[6] = 
{
	// ICON					Number of Icons
	EVT_LOSER,				// 0
	EVT_FEATURE_NEAR_MISS,	// 1
	EVT_TWO_GARDEN,			// 2
	EVT_THREE_GARDEN,		// 3
	EVT_FOUR_GARDEN,		// 4
	EVT_FIVE_GARDEN			// 5
};

////////////////////////////////////////////////////////////////////////////
/// EnchantedGardenGuts::g_aScatterMasks
/// A quick array for getting the ScatterMasks for each reel.
////////////////////////////////////////////////////////////////////////////
SlotGuts::ScatterMasks EnchantedGardenGuts::g_aScatterMasks[5] =
{
	SCATTER_REEL_0_MASK,	
	SCATTER_REEL_1_MASK,	
	SCATTER_REEL_2_MASK,	
	SCATTER_REEL_3_MASK,	
	SCATTER_REEL_4_MASK
};

#pragma endregion

#pragma region Construction & Destruction

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::RainDanceGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::EnchantedGardenGuts(void)
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// g_aLineWinSpinResults array. 
	C_ASSERT( countof( g_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::~RainDanceGuts()
///
/// Destructor for EnchantedGardenGuts. 
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::~EnchantedGardenGuts(void)
{
}

#pragma endregion


#pragma region Methods

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::preSpinClassifyResults( SlotSpin* spin )
{
	super::preSpinClassifyResults( spin );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare local a variable to track whether or not we found a substitution.
	bool bSubstitution = false;
	
	// Initialize out param
	multiplier = 1;

	// See what the spin result is, and if we used any substitution symbols.
	rtnresult = getLineWinResult( stops, testIcon, bSubstitution );

#ifdef _DEBUG
	SpinResult r = (SpinResult)rtnresult;
	ASSERT( r >= EVT_LOSER && r < NUM_VALID_SPIN_RESULTS );
#endif

	// If we did actually have a substitution and a winning spin
	if (  bSubstitution && rtnresult != EVT_LOSER)
	{
		multiplier *= NORMAL_MODE_SUBSTITUTION_MULTIPLIER;
		spinline->addNote( NOTE_SUBSTITUTION );
		spinline->addNote( NOTE_FAIRY_MULTIPLIER );
	}

	ASSERT( EVT_LOSER <= rtnresult && rtnresult <= EVT_FIVE_UNICORN );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Keep up with how many scatter icons where found.
	uint8 numScatterIcons = 0;

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See what the spin result is.
	SpinResult srResult = getScatterWinResult( spin, nScatterPositions, numScatterIcons );

	// Give them the scatter prize.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// See if they triggered the feature.
	evaluateFeatureTrigger( spin, srResult, nScatterPositions, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn		EnchantedGardenGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) || 
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::evaluateFeatureTrigger()
///
/// This function determines if the feature is triggered.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::evaluateFeatureTrigger( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions, uint8 numScatterIcons )
{
	// See if they had "firefly" symbols
	if ( getGameModeId() == MODE_FREE_SPIN )
	{
		const uint8 reelCount = getReelCount();

		// Collect fireflies
		int32 fireflyPositions = 0;
		int32 fireflyCount = getFireflyIconsOnSpin( spin, fireflyPositions );

		// 3 or more fireflies adds more free games
		if ( fireflyCount >= 3 )
		{
			uint8 reel = 0;

			//check each reel for a firefly
			while( reel < reelCount )
			{
				// each firefly adds free games
				if ( fireflyPositions & ( g_aScatterMasks[reel] ) )
				{
					// How many did we win
					int newFreeGames = getRandomValueFromProbabilityTable();

					// Should be between 1 & 3
					ASSERT( 0 < newFreeGames && newFreeGames <= 3 );

					// Add the Free Spins.
					incrementFreeSpins( newFreeGames );

					// Add a scatter to the spin for the feature trigger.
					spin->addScatter( fireflyPositions, 0, 0, false,
						EVT_FIREFLY_FEATURE, newFreeGames );
				}

				++reel;
			}
		}
	}

	// If there's at least one scatter icon...
	// FB Case 16102 - Turns out this game CANNOT retrigger the feature.
	if ( numScatterIcons && MODE_NORMAL == getGameModeId() )
	{
		//See if they triggered the free-spin feature.
		//Check for the scatterPositions to include reel 1
		//Without that, it's not even a near miss.
		if ( scatterPositions & SCATTER_REEL_0_MASK )
		{
			//Check for the presence of Fairy Princess on reel 5	
			const int32	 gameMode = getGameModeId();
			const uint8* stops = spin->getStops();	
			const int32  testReel = getReelCount() - 1;	

			// Have we found the princess yet?
			bool bFound = false;

			// Look at each row until we have
			for( int row = 0; !bFound && row < 3; ++row )
			{
				// Get the relative stop value
				uint8 stop = relativeWheelPosition( gameMode, testReel, stops[ testReel ], row );

				// See if this stop is the Fairy Princess
				bFound = ICON_FAIRY_PRINCESS == getIcon( gameMode, testReel, stop )->iconid;
			}

			if (bFound)
			{
				// They triggered the free-spin feature.
				// Make sure we are in free-spin mode.
				setGameModeId( MODE_FREE_SPIN );

				//Add the Free Spins.  There are 7 in this case.
				incrementFreeSpins( 7 );

				// Add a scatter to the spin for the feature trigger.
				spin->addScatter( scatterPositions, 0, 0, false,
					EVT_FREE_GAMES_FEATURE, 7 );
			}
			else
			{
				// then add a scatter to the spin for the feature near miss.
				spin->addScatter( scatterPositions, 0, 0, false,
					EVT_FEATURE_NEAR_MISS, 0 );
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::evaluateScatterWin(SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions)
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win
	// We will defer awarding the near miss also (see evaluateFeatureTrigger)
	if ( srScatterResult != EVT_LOSER 
		&& srScatterResult != EVT_FEATURE_NEAR_MISS )
	{
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;

		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Use the free game multiplier, if needed
		if ( MODE_FREE_SPIN == getGameModeId() )
		{
			nPayout *= FREE_SPIN_MODE_MULTIPLIER;
		}

		// Add the payout to the total win for this spin.
		increaseTotalWin( nPayout );

		// Add a scatter to the spin.
		spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), 1, false, srScatterResult, 0 );
	}
}


////////////////////////////////////////////////////////////////////////////
/// @fn		EnchantedGardenGuts::getScatterWinResult()
///
/// @param   spin				The spin we are evaluating.
/// @param   scatterPositions	An output parameter that contains a bit mask for the icon positions.
/// @param	 bSubstitition		An output parameter that is set to true if a substitution symbol was used.
/// @param	 numScatterIcons	An output parameter that is set to the number of scatter icons that were found.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for scatter wins.
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::SpinResult
EnchantedGardenGuts::getScatterWinResult( SlotSpin* spin, int32& scatterPositions, uint8& numScatterIcons ) const
{
	// Keep up with how many scatter icons we found.
	numScatterIcons = 0;
	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Keep up with whether or not we found a scatter icon on this stop.
			bool bFoundScatterIconOnReel = false;

			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is a garden...
			if ( currentIcon == ICON_GARDEN ||
				(currentIcon == ICON_FAIRY_PRINCESS && reel == getReelCount() - 1 ) )
			{
				// Set the flag so that we know we found a scatter icon on this reel.
				bFoundScatterIconOnReel = true;
			}

			// If we found a scatter icon...
			if ( bFoundScatterIconOnReel )
			{
				if( currentIcon == ICON_GARDEN )
				{
					// then increase the scatter icon count - only for GARDENS though
					numScatterIcons++;
				}

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;

		}
	}

	// Use the array to look up the spin result in the current game mode.
	return lookupScatterWinSpinResult( numScatterIcons );
}

int32
EnchantedGardenGuts::getFireflyIconsOnSpin( SlotSpin* spin, int32& fireflyPositions )
{
	// How many have we counted
	int32 fireflies = 0;

	// Save off the game mode...
	const int32 gameMode = getGameModeId();

	// If in free spin mode, we can have fireflies on some symbols
	if ( MODE_FREE_SPIN == gameMode )
	{
		const uint8* const stops = spin->getStops();
		const uint8 reelCount = getReelCount();
		const uint8 rowCount = getRowCount();

		// for each reel...
		for( uint8 reel = 0; reel < reelCount; ++reel )
		{
			// The position bit for the current icon in case it needs to be stored in a mask.
			uint8 position = reel;

			// ...and each row
			for( uint8 row = 0; row < rowCount; ++ row )
			{
				// Get the relative stop value
				uint8 stop = relativeWheelPosition( gameMode, reel, stops[ reel ], row );

				// See if this icon has a free game firefly
				if(  isFreeModeFireflyIcon( getIcon( getGameModeId(), reel, stop )->iconid ) )
				{
					++fireflies;
					fireflyPositions |= 1 << position;
				}
				
				// Move to the next bit in the position mask.
				position += 5;
			}
		}
	}

	return fireflies;
}

bool8
EnchantedGardenGuts::isFreeModeFireflyIcon( uint8 icon ) const
{
	return( icon == ICON_UNICORN || icon == ICON_GARDEN || icon == ICON_FAIRY_PRINCESS );
}

//////////////////////////////////////////////////////////////////////////
/// @fn		EnchantedGardenGuts::getLineWinResult()
///
/// @param   stops				The line stops we are evaluating.
/// @param   testIcon			Which icon we are looking for in this line.
/// @param	 bDolphinSubstitution		An output parameter to indicate if a Dolphin substitution symbol was used.
/// @param	 bBonusFishSubstitution		An output parameter to indicate if a Fish substitution symbol was used.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
///	This function looks for line wins.
////////////////////////////////////////////////////////////////////////////
EnchantedGardenGuts::SpinResult 
EnchantedGardenGuts::getLineWinResult( uint8* stops, uint8 testIcon, 
									 bool& bSubstitution /*out value*/)
{
	//Since the substitution icon may not appear on reel one,
	//and this game is left-to-right only,
	//We assume that we will never be using it as the test icon.
	ASSERT( testIcon != ICON_FAIRY_PRINCESS );

	// Initialize the out parameter
	bSubstitution = false;

	uint8 gameMode  = getGameModeId();
	uint8 reelCount = getReelCount();

	// Loop through the remaining reels until the streak is over
	for ( uint8 i = 1; i < reelCount; ++i )
	{
		// See what this icon is.
		uint8 currentIcon = getIcon( gameMode, i, stops[i] )->iconid;

		// If the current icon is a Fairy Princess
		if ( currentIcon == ICON_FAIRY_PRINCESS )
		{
			#ifdef DEBUG
				//Check assumptions /*TODO*/
			#endif

		    // Doesn't substitute for Garden
			if ( testIcon != ICON_GARDEN )
			{
				// then use it as a substitution symbol and continue on.				
				bSubstitution = true;
				continue;
			}
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::Debug_SetLoserStops()   
///
/// @param	 pStops      Array into which to put the stop values    
///
/// This overridden method returns a losing set of stop values
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=5;
	pStops[1]=7;
	pStops[2]=17;
	pStops[3]=4;
	pStops[4]=10;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 EnchantedGardenGuts::isValidGuts()
{
	// Make sure that the g_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( g_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the g_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 scatter icons.
	ASSERT( countof( g_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that all game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
	return ( getGutsId() >= EnchantedGardenGuts::VALID_GUTS_RANGE_BEGIN && getGutsId() < EnchantedGardenGuts::VALID_GUTS_RANGE_END );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 EnchantedGardenGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param	 nGameModeMultiplier the current multiplier
///
/// @param	 nNoteId the note id to assign
///
/// This overridden method returns the multiplier and note ID for the current game mode.
////////////////////////////////////////////////////////////////////////////
void EnchantedGardenGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
{
	//Free games...
	if( getGameModeId() == MODE_FREE_SPIN )
	{
		// ...are tripled...
		nGameModeMultiplier = FREE_SPIN_MODE_MULTIPLIER;

		//...and here's the note.
		nNoteId = NOTE_FREE_GAMES_MULTIPLIER;
	}
}

#pragma endregion

